package com.osdp.common.security.config;

import com.osdp.auth.api.IAuthApi;
import com.osdp.auth.api.ITokenApi;
import com.osdp.common.security.filter.TokenVerifyFilter;
import com.osdp.common.util.DefaultPasswordEncoder;
import com.osdp.sc.api.IPermissionApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * https://www.jianshu.com/p/77b4835b6e8e
 * @EnableWebSecurity 开启权限验证注解
 * prePostEnabled = true  开启方法级别的权限谁
 * securedEnabled = true, 注解是用来定义业务方法的安全配置。在需要安全[角色/权限等]的方法上指定 @Secured，
 * 并且只有那些角色/权限的用户才可以调用该方法,@Secured缺点（限制）就是不支持Spring EL表达式。不够灵活。
 * 并且指定的角色必须以ROLE_开头，不可省略。
 */
@EnableWebSecurity
@Configurable
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Resource
    private UserDetailsService userDetailsService;

    @Autowired
    private ITokenApi tokenApi;

    @Autowired
    private IPermissionApi permissionApi;

    @Bean
    public PasswordEncoder passwordEncoder(){
        return DefaultPasswordEncoder.getInstance();
    }
    /**
     身份认证过滤器，认证管理器：
     1. 认证信息提供方式（用户名，密码，当前用户的资源权限）
     2. 可采的内存存储方式，也可采用数据库方式等。
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(this.userDetailsService).passwordEncoder(passwordEncoder());
    }
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    /**
     资源权限配置（过滤器链）
     1. 拦截哪些资源
     2. 资源所对应的角色权限
     3. 定义认证方式 httpbasic  httpform
     4. 定制登录页面，登录请求地址，错误处理方式
     5. 自定义 springsecurity 过滤器等
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.cors();
        http.headers().frameOptions().disable();
        http.addFilter(new TokenVerifyFilter(super.authenticationManager(),tokenApi));
        // 关闭csrf
        http.csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 使用 JWT，关闭session
                .and().authorizeRequests() //认证请求
                .anyRequest().access("@rbacauthorityservice.hasPermission(request,authentication)"); // RBAC 动态 url 认证
    }

    /**
     * 需要忽略的url资源
     */
    @Value("#{'${osdp.ignore.patterns}'.split(',')}")
    private List<String> ignores;

    @Override
    public void configure(WebSecurity web) {
        WebSecurity.IgnoredRequestConfigurer ignoring = web.ignoring();
        if(!CollectionUtils.isEmpty(ignores)){
            ignores.forEach(ignoring::antMatchers);
        }
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
        return source;
    }


    @Component("rbacauthorityservice")
    public class RbacAuthorityService {
        private final static String ROLE_ANONYMOUS = "ROLE_ANONYMOUS";

        public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
            Set<String> authoritys = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
            if (authoritys.contains(ROLE_ANONYMOUS)){ return true;}
            return permissionApi.allow(request.getRequestURI(), request.getMethod(), authoritys);
        }
    }
}

